﻿using System.Data;
using System.Net;
using System.Reflection;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.AspNetCore.Routing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
using NetTools;

namespace Devonline.Identity;

/// <summary>
/// 用户认证授权相关基础服务
/// 
/// v1. 授权服务的设计思路: 
/// 查询出用户的所有身份和授权数据保存到缓存中, 每次请求时, 从缓存中再次读取并验证受否有权限
/// 此方案: 设计简单, 但效率不高, 因为每次读写缓存, 查询, 需要序列化和反序列化的数据量过大, 性能损耗较大
/// 
/// v2. 新授权服务的设计思路: 
/// 第一次查询并计算出用户所有身份和授权数据, 保存到缓存中
/// 并且同时保存: 系统+用户+权限 构成的键值对到缓存中, 以及缓存顶级系统所有数据
/// 每次验证权限时, 再次使用 系统+用户+权限 来作为键读取缓存进行验证
/// 此方案: 设计复杂, 但效率高, 对开发调试和授权配置更友好
/// 
/// 用户每次请求来查询时, 已知条件均在 HttpContext 中, 其中 UserName, System, Request.Path, Request.HttpMethod 为最关键信息, 但 Path 存在省略和路由参数, HttpMethod 为不确定信息
/// 因此保存的键结构为: 
///     System@userName@Request.Path<@HttpMethod>  如果不同 HttpMethod 同一个接口地址的接口存在, 则需要增加 @HttpMethod 来区分
///     System@userName@Resource.Code
/// 保存的值为: AccessRule(包含完整 Resource, 用以校验是否存在多个), 仅优先级最高的一条
/// 在此基础上, 还需要维护一个用户 <-> 授权资源键的列表, 用于用户资源变更时刷新缓存
/// </summary>
public class AuthorizationService
{
    private readonly HttpSetting _httpSetting;
    private readonly ILogger<AuthorizationService> _logger;
    private readonly IDistributedCache _cache;
    private readonly IdentityDbContext _context;
    private readonly LanguageService _languageService;
    private readonly HttpContext _httpContext;
    private readonly HttpRequest _request;
    private readonly RouteValueDictionary _routeValues;
    private readonly string _userName;
    private readonly string _ipAddress;
    /// <summary>
    /// 用户访问序号, 每次访问分配的序号, 用于在日志中标记用户单次访问的过程唯一性, 此值由 HttpContext.TraceIdentifier 提供
    /// </summary>
    private readonly string _userAccess;
    private readonly DistributedCacheEntryOptions? _cacheEntryOptions;

    public AuthorizationService(
        HttpSetting httpSetting,
        ILogger<AuthorizationService> logger,
        IHttpContextAccessor httpContext,
        IDistributedCache cache,
        IdentityDbContext context,
        LanguageService languageService
        )
    {
        ArgumentNullException.ThrowIfNull(httpContext.HttpContext);
        ArgumentNullException.ThrowIfNull(httpSetting.Cache);
        _httpSetting = httpSetting;
        _logger = logger;
        _httpContext = httpContext.HttpContext;
        _request = _httpContext.Request;
        _cache = cache;
        _context = context;
        _languageService = languageService;
        _routeValues = _request.RouteValues;
        _userName = _httpContext.GetUserName();
        _ipAddress = _request.GetRequestIpAddress();
        _cacheEntryOptions = new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(httpSetting.Cache.ExpireTime) };
        _userAccess = $"User {_userName} in access index {_httpContext.TraceIdentifier}";
    }

    #region 基于 AccessAuthorize 注解的授权检查
    /// <summary>
    /// step1. 校验授权
    /// </summary>
    /// <returns></returns>
    public async Task<bool> AuthorizeAsync()
    {
        var _userName = _httpContext.GetUserName();
        _logger.LogInformation($"{_userAccess}: from {_ipAddress} now will validate authorization for access resource: {_httpContext.Request.GetRequestUri()}");

        if (_httpSetting.AuthorizeIpAddress && (!await AuthorizeIpAddress()))
        {
            _logger.LogInformation($"{_userAccess}: from {_ipAddress} is forbidden to access!");
            return false;
        }

        var accessAuthorizeCode = GetAccessAuthorizeCode();
        if (string.IsNullOrWhiteSpace(accessAuthorizeCode))
        {
            _logger.LogInformation($"{_userAccess}: authentication has been initialed, but still not!");
            return false;
        }

        var userAuthorizationCache = new UserAuthorizationContext { UserName = _userName, Code = accessAuthorizeCode };

        //1. 查询用户状态缓存验证授权是否初始化
        var authenticationStateCacheKey = userAuthorizationCache.GetAuthenticationStateCacheKey();
        var userAuthenticationState = await _cache.GetValueAsync<UserAuthenticationState>(authenticationStateCacheKey);
        if (userAuthenticationState is null)
        {
            _logger.LogInformation($"{_userAccess}: has not initialed, now will initial first!");
            var userContext = await GetUserContextAsync();
            userAuthenticationState = new UserAuthenticationState(_userName, userContext.IsAuthenticated, userContext.IsAuthorizeAccessor);
        }

        if (userAuthenticationState is null)
        {
            _logger.LogInformation($"{_userAccess}: authentication has been initialed, but still not!");
            return false;
        }

        //此验证可能因为最早构造的 UserContext 因为登录了但是未在IP地址白名单被拦截而未成功认证, 因此此处不在判断
        //if (!userAuthenticationState.IsAuthenticated)
        //{
        //    _logger.LogInformation($"{_userAccess}: has not authenticated, can not access any authorized resource!");
        //    return false;
        //}

        //2. 查询用户授权缓存, 验证资源访问授权
        var userAuthorizationCacheKey = userAuthorizationCache.GetCacheKey();
        _logger.LogInformation($"{_userAccess}: will validate resource {userAuthorizationCacheKey}");
        var userAuthorization = await _cache.GetValueAsync<UserAuthorizationContext>(userAuthorizationCacheKey);
        if (userAuthorization is null)
        {
            _logger.LogWarning($"{_userAccess}: has no authorization information for resource {userAuthorizationCacheKey}, access failed!");
            return false;
        }

        //3. 授权访问, 条件是: 系统开启访问授权功能, 且当前用户是需要授权访问的用户, 且当前资源需要授权访问
        if (userAuthenticationState.IsAuthorizeAccessor && userAuthorization.IsAuthorizeAccess.HasValue && userAuthorization.IsAuthorizeAccess.Value)
        {
            _logger.LogInformation($"{_userAccess}: need to be authorized access first!");
            if (userAuthorization.HasAuthorizeAccess is null)
            {
                if (string.IsNullOrWhiteSpace(_httpSetting.UserInteraction?.AuthorizedAccess))
                {
                    _logger.LogWarning($"{_userAccess}: in system has no authorized access page configurated!");
                    return false;
                }

                _logger.LogInformation($"{_userAccess}: have not get authorized access, please redirect to authorized access page to apply authorization first!");
                _httpContext.Response.Redirect(_httpSetting.UserInteraction!.AuthorizedAccess);
                return false;
            }

            if (!userAuthorization.HasAuthorizeAccess.Value)
            {
                //已授权拒绝
                _logger.LogInformation($"{_userAccess}: have been already denied, forbidden access!");
                return false;
            }

            //已授权访问
            _logger.LogInformation($"{_userAccess}: has been already got authorized access!");
        }

        //4. 验证访问规则
        if (userAuthorization.AccessCount.HasValue && userAuthorization.AccessCount.Value <= 0)
        {
            //访问次数限制已达到, 不可访问
            _logger.LogInformation($"{_userAccess}: the access count limit has been reached, can not access current resource!");
            return false;
        }

        if (userAuthorization.ExpireTime.HasValue && userAuthorization.ExpireTime.Value >= DateTime.UtcNow)
        {
            //可访问时间限制已达到, 不可访问
            _logger.LogInformation($"{_userAccess}: the access expire time limit has been reached, can not access current resource!");
            return false;
        }

        if (userAuthorization.HasAccessCondition.HasValue && userAuthorization.HasAccessCondition.Value && !await AuthorizeAccessRuleAsync(userAuthorization))
        {
            //未匹配访问条件限制
            _logger.LogInformation($"{_userAccess}: not match all the accress rules, can not access current resource!");
            return false;
        }

        //5. 全部验证通过则放行
        _logger.LogInformation($"{_userAccess}: from {_ipAddress} access {accessAuthorizeCode} has been allowed to access resource!");
        return true;
    }
    /// <summary>
    /// step2. 初始化用户可访问授权数据缓存
    /// 用户可访问授权数据缓存, 包括三个数据缓存
    /// 1. 用户信息缓存, key: CACHE_USER_{userName}_UserContext, 一个完整的 UserContext
    /// 1. 用户信息缓存, key: CACHE_USER_{userName}_Authorize_Initial, 一个完整的 UserContext
    /// 2. 用户已授权资源缓存列表: 一组授权校验缓存
    ///     keys: CACHE_USER_{userName}_{system}_{path}<_{httpMethod}>
    ///     values: {}
    /// </summary>
    /// <returns></returns>
    public async Task<UserContext> GetUserContextAsync(string? userName = default)
    {
        userName ??= _userName;
        _logger.LogInformation($"{_userAccess}: get user {userName} context from cache!");

        var userAuthorization = new UserAuthorizationContext { UserName = userName };
        var authenticationStateCacheKey = userAuthorization.GetAuthenticationStateCacheKey();
        var userContextCacheKey = userAuthorization.GetUserContextCacheKey();
        var userAuthenticationState = await _cache.GetValueAsync<UserAuthenticationState>(authenticationStateCacheKey);
        var userContext = await _cache.GetValueAsync<UserContext>(userContextCacheKey);
        if (userAuthenticationState is not null && userContext is not null)
        {
            return userContext;
        }

        _logger.LogInformation($"{_userAccess}: get user {userName} context from database!");
        var isAuthenticated = _httpContext.User.Identity?.IsAuthenticated ?? false;
        var user = await _context.Users.AsNoTracking().FirstOrDefaultAsync(x => x.State == DataState.Available && x.UserName != null && x.UserName.ToLower() == userName.ToLower());
        if (isAuthenticated && user is null)
        {
            //用户已登录, 但未创建用户信息, 此时说明使用了第三方账户登录了系统, 尚未在本系统创建账户
            _logger.LogInformation($"{_userAccess}: have no user info exist, will get from oauth user!");
            var oauthUser = await _context.OAuthUsers.AsNoTracking().FirstOrDefaultAsync(x => x.Id == userName || x.OpenId == userName);
            if (oauthUser is null)
            {
                _logger.LogWarning($"{_userAccess}: neither user info nor oauth user info, that was abnormal!");
                throw new BadHttpRequestException($"用户 {userName} 不存在或已删除!");
            }

            //登录第三方账户后, 经过查找, 第三方账户尚未关联到系统账户, 此时还未创建用户信息或尚未对应到用户信息
            var name = _httpContext.GetUserNameIdentifier() ?? userName;
            user = new User { Id = userName, Name = name, UserName = userName, Type = AuthorizeType.Authenticator, State = DataState.Draft };
            _logger.LogWarning($"{_userAccess}: can not create user info, will new draft user info for user {userName}");
        }

        //脱敏
        if (user is null)
        {
            _logger.LogWarning($"{_userAccess}: user {userName} not exist!");
            throw new BadHttpRequestException($"用户 {userName} 不存在");
        }

        user.PasswordHash = null;

        //1. 获取用户直属组织身份
        GroupViewModel? groupViewModel = null;
        if (!string.IsNullOrWhiteSpace(user.GroupId))
        {
            _logger.LogInformation($"{_userAccess}: get user {userName} group directly affiliated from database!");
            groupViewModel = (await _context.Groups.AsNoTracking().FirstOrDefaultAsync(x => x.State == DataState.Available && x.Id == user.GroupId))?.CopyTo<GroupViewModel>();
        }

        //2. 获取用户级别身份
        LevelViewModel? levelViewModel = null;
        if (!string.IsNullOrWhiteSpace(user.LevelId))
        {
            _logger.LogInformation($"{_userAccess}: get user {userName} level from database!");
            levelViewModel = (await _context.Levels.AsNoTracking().FirstOrDefaultAsync(x => x.State == DataState.Available && x.Id == user.LevelId))?.CopyTo<LevelViewModel>();
        }

        //3. 获取身份选择数据
        //Dictionary<OwnerType, string[]>? identityId = null;
        //identityId = await _cache.GetValueAsync<Dictionary<OwnerType, string[]>>(CACHE_USER_IDENTITY_TYPE + userName);

        userContext = new UserContext
        {
            IsAuthenticated = isAuthenticated,
            User = user.CopyTo<UserViewModel>(),
            Group = groupViewModel,
            Level = levelViewModel,
            Roles = [],
            Groups = [],
            Resources = [],
            ResourceTrees = []
        };

        //4. 获取用户角色身份
        _logger.LogInformation($"{_userAccess}: get user {userName} roles from database!");
        var roleIds = await _context.UserRoles.AsNoTracking().Where(x => x.UserId.Equals(user.Id)).Select(x => x.RoleId).Distinct().ToListAsync();
        if (roleIds.IsNotNullOrEmpty())
        {
            var roles = _context.Roles.AsNoTracking().Where(x => x.State == DataState.Available && roleIds.Contains(x.Id)).Select(x => new RoleViewModel
            {
                Id = x.Id,
                Alias = x.Alias,
                Image = x.Image,
                Description = x.Description,
                Name = x.Name!,
                NormalizedName = x.NormalizedName,
                State = x.State,
                Type = x.Type
            }).ToList();

            if (roles.IsNotNullOrEmpty())
            {
                userContext.Roles.AddRange(roles);
            }
        }

        //4.1 一旦登录, 默认给到认证用户的角色, 但不重复添加认证用户
        if (userContext.IsAuthenticated && userContext.User.Type >= AuthorizeType.Authenticator && !userContext.Roles.Any(x => x.Name == GROUP_AUTHENTICATOR))
        {
            _logger.LogInformation($"{_userAccess}: get user {userName} Authenticator identity role from database!");
            var role = await _context.Roles.AsNoTracking().Where(x => x.State == DataState.Available && x.Type == AuthorizeType.Authenticator && x.Name == GROUP_AUTHENTICATOR).Select(x => new RoleViewModel
            {
                Id = x.Id,
                Alias = x.Alias,
                Image = x.Image,
                Description = x.Description,
                Name = x.Name!,
                NormalizedName = x.NormalizedName,
                State = x.State,
                Type = x.Type
            }).FirstOrDefaultAsync();

            if (role is not null)
            {
                userContext.Roles.Add(role);
                if (userContext.Roles.Any(x => x.Name == _httpSetting.IdentityGroupAuthorizedUser))
                {
                    userAuthorization.IsRealNameAuthentication = true;
                }
            }
        }

        //5. 获取用户组织身份
        _logger.LogInformation($"{_userAccess}: get user {userName} groups from database!");
        var groupIds = await _context.UserGroups.AsNoTracking().Where(x => x.UserId != null && x.UserId.Equals(user.Id)).Select(x => x.GroupId).Distinct().ToListAsync();
        if (groupIds is not null && groupIds.Count != 0)
        {
            var userGroups = new List<GroupViewModel>();
            foreach (var groupId in groupIds)
            {
                if (groupId is not null)
                {
                    var groups = await _context.GetParentsAsync<IdentityDbContext, Group>(groupId);
                    if (groups.IsNotNullOrEmpty())
                    {
                        userGroups.AddRange(groups.CopyTo<List<GroupViewModel>>());
                    }
                }
            }

            if (userGroups.Count != 0)
            {
                userContext.Groups.AddRange(userGroups);
            }
        }

        //6. 获取用户访问规则及可访问资源
        _logger.LogInformation($"{_userAccess}: will get user {userName} authorized access resources from database!");
        await GetUserAccessResources(userContext);

        //7. 用户授权上下文信息写入缓存
        _logger.LogInformation($"{_userAccess}: set user {userName} UserContext to cache!");
        await _cache.SetValueAsync(userContextCacheKey, userContext, _cacheEntryOptions);

        //8. 写入用户初始化缓存
        _logger.LogInformation($"{_userAccess}: set user {userName} UserAuthenticationState to cache!");
        userAuthenticationState = new UserAuthenticationState(userName, isAuthenticated, userContext.IsAuthorizeAccessor);
        await _cache.SetValueAsync(authenticationStateCacheKey, userAuthenticationState, _cacheEntryOptions);

        return userContext;
    }

    /// <summary>
    /// 根据身份类型和编号获取对应的用户
    /// </summary>
    /// <param name="identityType">身份分配类型</param>
    /// <param name="identityIds">身份编号</param>
    public async Task<ICollection<User>?> GetIdentityUsers(IdentityType identityType = IdentityType.User, params string[] identityIds)
    {
        var queryable = _context.Users.AsNoTracking().Where(x => x.State == DataState.Available);
        if (identityIds.Length == 0)
        {
            return await queryable.ToListAsync();
        }

        return identityType switch
        {
            IdentityType.User => await queryable.Where(x => identityIds.Contains(x.Id) || identityIds.Contains(x.UserName)).ToListAsync(),
            IdentityType.Role => await queryable.Where(x => _context.UserRoles.AsNoTracking().Where(a => identityIds.Contains(a.RoleId)).Select(a => a.UserId).Contains(x.Id)).ToListAsync(),
            IdentityType.Group => await queryable.Where(x => _context.UserGroups.AsNoTracking().Where(a => identityIds.Contains(a.GroupId)).Select(a => a.UserId).Contains(x.Id)).ToListAsync(),
            IdentityType.Level => await queryable.Where(x => identityIds.Contains(x.LevelId)).ToListAsync(),
            IdentityType.System => await queryable.Where(x => x.Type == AuthorizeType.System && x.UserName == USER_SYSTEM).ToListAsync(),
            IdentityType.Anonymous => await queryable.Where(x => x.Type == AuthorizeType.Anonymous && x.UserName == USER_ANONYMOUS).ToListAsync(),
            _ => await queryable.ToListAsync(),
        };
    }
    /// <summary>
    /// 给当前用户自动添加角色, 适用于特殊情况下的设置角色, 只能设置内部角色类型更低的类型
    /// 设置多个角色的情况下, 仅成功设置的生效
    /// </summary>
    /// <param name="roleNames">角色名, 角色不能是内部角色以上类型的角色</param>
    /// <returns></returns>
    public async Task<bool> AutoRoleAsync(params string[] roleNames)
    {
        var names = roleNames.ToString<string>();
        _logger.LogInformation("user {user} will be auto assigned to roles {roles}", _userName, names);
        var user = await _context.Users.FirstOrDefaultAsync(x => x.UserName == _userName);
        if (user is null)
        {
            _logger.LogWarning($"当前用户 {_userName} 不存在!");
            return false;
        }

        if (user.State != DataState.Available)
        {
            _logger.LogWarning($"当前用户 {_userName} 不可用!");
            return false;
        }

        var roles = await _context.Roles.Where(x => x.Name != null && roleNames.Contains(x.Name)).ToListAsync();
        if (roles is null || roles.Count == 0)
        {
            _logger.LogWarning($"当前角色 {names} 不存在!");
            return false;
        }

        var userRoles = new List<UserRole>();
        foreach (var role in roles)
        {
            if (role.State != DataState.Available)
            {
                _logger.LogWarning($"当前角色 {role.Name} 不可用!");
                continue;
            }

            //自动设定角色, 只能设置角色类型低于 内部(Internal) 角色的类型
            if (role.Type >= AuthorizeType.Internal)
            {
                _logger.LogWarning($"当前角色 {role.Name} 不可进行自动设置!");
                continue;
            }

            var userRole = await _context.UserRoles.FirstOrDefaultAsync(x => x.UserId == user.Id && x.RoleId == role.Id);
            if (userRole is not null)
            {
                _logger.LogInformation($"当前用户 {_userName} 已经是 {role.Name} 角色了!");
                continue;
            }

            userRoles.Add(new UserRole { UserId = user.Id, RoleId = role.Id });
        }

        await _context.UserRoles.AddRangeAsync(userRoles);
        var result = await _context.SaveChangesAsync();
        if (result > 0)
        {
            _logger.LogWarning($"当前用户 {_userName} 已经成功设置 {names} 角色!");

            //刷新用户缓存
            await GetUserContextAsync();
            return true;
        }

        _logger.LogWarning($"当前用户 {_userName} 未能成功设置 {names} 角色!");
        return false;
    }
    /// <summary>
    /// 清空指定身份成员的缓存
    /// </summary>
    /// <param name="identityType">身份类型</param>
    /// <param name="identityIds">身份编号</param>
    /// <returns></returns>
    public async Task ClearCacheAsync(IdentityType identityType, params string[] identityIds)
    {
        _logger.LogInformation($"{_userAccess}: will clear identity {identityType} ids {identityIds} cache!");
        var users = await GetIdentityUsers(identityType, identityIds);
        if (users is not null && users.Count != 0)
        {
            foreach (var user in users)
            {
                await ClearCacheAsync(user.UserName!);
            }

            _logger.LogInformation($"{_userAccess}: clear identity {identityType} ids {identityIds} cache finished!");
        }
    }
    /// <summary>
    /// 清空用户缓存
    /// </summary>
    /// <returns></returns>
    public async Task ClearCacheAsync(string userName)
    {
        _logger.LogInformation($"{_userAccess}: will clear user cache!");
        var userContext = await GetUserContextAsync(userName);
        if (userContext is not null && userContext.Resources.Count != 0)
        {
            var userAuthorization = new UserAuthorizationContext { UserName = userName };
            await _cache.RemoveAsync(userAuthorization.GetAuthenticationStateCacheKey());
            await _cache.RemoveAsync(userAuthorization.GetUserContextCacheKey());
            foreach (var resource in userContext.Resources)
            {
                await _cache.RemoveAsync(new UserAuthorizationContext { UserName = userName, Code = resource.Code }.GetCacheKey());
            }

            _logger.LogInformation($"{_userAccess}: clear user cache finished!");
        }
    }
    #endregion

    #region 用户信息相关
    /// <summary>
    /// 获取身份访问规则及可访问资源
    /// </summary>
    /// <param name="identityType">身份类型</param>
    /// <param name="identityId">身份编号</param>
    /// <returns></returns>
    public async Task<ICollection<ResourceViewModel>> GetIdentityAccessResources(IdentityType identityType, string identityId)
    {
        _logger.LogInformation($"{_userAccess}: get identity {identityType} id {identityId} authorized access resources started!");
        var systemResources = await GetAllSystemResourceTreeAsync();
        var accessRules = await GetIdentityAccessRulesAsync(identityType, identityId);
        var accessResources = GetSystemAccessResources(systemResources, accessRules);
        var resourcesTrees = GetAccessResourceTrees(systemResources, accessResources);
        _logger.LogInformation($"{_userAccess}: get identity {identityType} id {identityId} authorized access resources finished!");

        return resourcesTrees;
    }
    /// <summary>
    /// 获取用户信息
    /// </summary>
    /// <param name="includeResources">是否同时获取用户授权资源</param>
    /// <returns></returns>
    public async Task<UserInfo?> GetUserInfoAsync()
    {
        var userContext = await GetUserContextAsync();
        if (userContext is not null && userContext.User is not null)
        {
            var userInfo = new UserInfo
            {
                User = userContext.User.Desensitize(),
                Group = userContext.Group,
                Resources = GetUserResources(userContext),
                Dcs = _httpSetting.Dcs,
                Language = await _languageService.GetLanguageAsync()
            };

            _logger.LogInformation($"user {userInfo.User.UserName} get userinfo from Identity Service success");
            return userInfo;
        }

        return default;
    }
    /// <summary>
    /// 获取用户信息
    /// </summary>
    /// <param name="includeResources">是否同时获取用户授权资源</param>
    /// <returns></returns>
    public async Task<UserInfo?> GetUserInfoAsync(string userName)
    {
        var userContext = await GetUserContextAsync(userName);
        if (userContext is not null && userContext.User is not null)
        {
            var userInfo = new UserInfo
            {
                User = userContext.User.Desensitize(),
                Group = userContext.Group,
                ResourceTree = userContext.ResourceTrees,
                Dcs = _httpSetting.Dcs,
                Language = await _languageService.GetLanguageAsync()
            };

            _logger.LogInformation($"user {userInfo.User.UserName} get userinfo from Identity Service success");
            return userInfo;
        }

        return default;
    }
    /// <summary>
    /// 根据身份信息刷新用户授权相关缓存
    /// </summary>
    /// <returns></returns>
    public async Task RefreshUserInfoAsync(IdentityType identityType, string identityId, Func<string, Task> notify)
    {
        var users = await GetIdentityUsers(identityType, identityId);
        if (users is not null && users.Count != 0)
        {
            foreach (var user in users)
            {
                //更新用户授权相关缓存
                await GetUserContextAsync(user.UserName);

                //强制推送客户端更新
                await notify(user.UserName!);
            }
        }
    }

    /// <summary>
    /// 从用户上下文得到客户端需要的用户信息
    /// </summary>
    /// <param name="userContext"></param>
    /// <returns></returns>
    private ICollection<ResourceViewModel>? GetUserResources(UserContext userContext)
    {
        ResourceViewModel? top = null;
        var accessAuthorizeCode = GetAccessAuthorizeCode();
        if (!string.IsNullOrWhiteSpace(accessAuthorizeCode))
        {
            var resource = userContext.Resources.FirstOrDefault(x => x.Code == accessAuthorizeCode);
            if (resource is not null)
            {
                top = GetTopResource(userContext.ResourceTrees, resource.SystemId!);
                if (top is not null)
                {
                    CleanResourceTree(top);
                }
            }
        }

        var resources = top?.Children;
        if (resources is not null && resources.Count == UNIT_ONE && resources.First().Children?.Count != UNIT_ZERO)
        {
            //仅有一个大菜单且子菜单数量不为 0 时, 将子菜单展开
            resources = resources.FirstOrDefault()?.Children;
        }

        return resources;
    }
    /// <summary>
    /// 从 ResourceTrees 中找到当前系统的权限树
    /// </summary>
    /// <param name="resourceTree">系统全部权限树</param>
    /// <param name="topId">当前系统的顶级节点资源编号</param>
    private ResourceViewModel? GetResourceTree(ICollection<ResourceViewModel> resourceTree, string topId)
    {
        foreach (var resource in resourceTree)
        {
            if (resource.Id == topId)
            {
                return resource;
            }

            if (resource.Children is not null && resource.Children.Count > 0)
            {
                var result = GetResourceTree(resource.Children, topId);
                if (result is not null)
                {
                    return result;
                }
            }
        }

        return null;
    }
    /// <summary>
    /// 过滤权限树, 只保留可在页面显示部分
    /// </summary>
    /// <param name="resource"></param>
    private void CleanResourceTree(ResourceViewModel resource)
    {
        resource.AccessRules = null;
        if (resource.Children is not null && resource.Children.Count != 0)
        {
            foreach (var child in resource.Children)
            {
                if ((child.ResourceType == ResourceType.Module || child.ResourceType == ResourceType.Page || child.ResourceType == ResourceType.Element) && child.Display)
                {
                    CleanResourceTree(child);
                }
            }

            resource.Children = resource.Children.Where(x => ((x.ResourceType == ResourceType.Module || x.ResourceType == ResourceType.Page || x.ResourceType == ResourceType.Element) && x.Display) && (x.HasPermission || (x.Children?.Any() ?? false))).ToList();
        }
    }
    /// <summary>
    /// 获取用户权限树的顶级系统资源对象
    /// </summary>
    /// <param name="resourceViewModels"></param>
    /// <param name="system"></param>
    /// <returns></returns>
    private ResourceViewModel? GetTopResource(ICollection<ResourceViewModel> resourceViewModels, string system)
    {
        var top = resourceViewModels.FirstOrDefault(x => x.ResourceType == ResourceType.System && (x.Id == system || x.Content == system));
        if (top is not null)
        {
            return top.Copy();
        }

        foreach (var resource in resourceViewModels)
        {
            if (resource.ResourceType == ResourceType.System && (resource.Children?.Any() ?? false))
            {
                top = GetTopResource(resource.Children, system);
                if (top is not null)
                {
                    return top.Copy(); ;
                }
            }
        }

        return null;
    }
    #endregion

    #region AccessAuthorize 授权相关数据初始化
    /// <summary>
    /// 从程序集中类型和方法配置的 AccessAuthorizeAttribute 获取所有资源
    /// </summary>
    /// <param name="assembly">程序集初始化的当前系统</param>
    /// <param name="system">当前系统资源</param>
    /// <returns></returns>
    public async Task InitialAccessAuthorizesAsync(Assembly assembly, Resource system)
    {
        var accessAuthorizes = GetAccessAuthorizes(assembly, system);
        if (accessAuthorizes is null || !accessAuthorizes.Any())
        {
            return;
        }

        _logger.LogInformation("Authorization Service will generate resources from AccessAuthorizes");
        var roleAdministrator = await _context.Roles.FirstOrDefaultAsync(x => x.Type == AuthorizeType.Administrator && x.Name == GROUP_ADMINISTRATOR) ?? throw new KeyNotFoundException(GROUP_ADMINISTRATOR);
        var roleDeveloper = await _context.Roles.FirstOrDefaultAsync(x => x.Type == AuthorizeType.Developer && x.Name == GROUP_DEVELOPER) ?? throw new KeyNotFoundException(GROUP_DEVELOPER);
        var userAnonymous = await _context.Users.FirstOrDefaultAsync(x => x.Type == AuthorizeType.Anonymous && x.UserName == USER_ANONYMOUS) ?? throw new KeyNotFoundException(USER_ANONYMOUS);
        var userSystem = await _context.Users.FirstOrDefaultAsync(x => x.Type == AuthorizeType.System && x.UserName == USER_SYSTEM) ?? throw new KeyNotFoundException(USER_SYSTEM);
        var roleAuthenticator = await _context.Roles.FirstOrDefaultAsync(x => x.Type == AuthorizeType.Authenticator && x.Name == GROUP_AUTHENTICATOR) ?? throw new KeyNotFoundException(GROUP_AUTHENTICATOR);

        system.Title = system.Name!;
        system.Alias = system.Name;
        system.Description = system.Name;
        system.OwnerType = IdentityType.Role;
        system.OwnerId = roleAdministrator.Id;
        system.ResourceType = ResourceType.System;
        system.AccessLevel = AccessLevel.Internal;
        system.Create(USER_SYSTEM);
        system.Update(USER_SYSTEM);

        var accessRules = new List<AccessRule> { AccessRuleSystemForbid(system), AccessRuleSystemAllow(system, roleAdministrator), AccessRuleSystemAllow(system, roleDeveloper) };
        var resources = new List<Resource> { system };
        resources.AddRange(await InitialResourcesAsync(accessAuthorizes, accessRules, system, userAnonymous, userSystem, roleAuthenticator, roleAdministrator));

        await RemoveExistAsync(system);
        await _context.Resources.AddRangeAsync(resources);
        await _context.AccessRules.AddRangeAsync(accessRules);
        await _context.SaveChangesAsync();
        _logger.LogInformation("Authorization Service generate resources from AccessAuthorizes success");
    }
    /// <summary>
    /// 根据当前上下文数据, 计算资源列表
    /// </summary>
    /// <param name="accessAuthorizes"></param>
    /// <param name="accessRules"></param>
    /// <param name="parent"></param>
    /// <param name="userAnonymous"></param>
    /// <param name="userSystem"></param>
    /// <param name="roleAuthenticator"></param>
    /// <param name="roleAdministrator"></param>
    /// <returns></returns>
    private async Task<List<Resource>> InitialResourcesAsync(List<AccessAuthorizeAttribute> accessAuthorizes, List<AccessRule> accessRules, Resource parent, User userAnonymous, User userSystem, Role roleAuthenticator, Role roleAdministrator)
    {
        var resources = new List<Resource>();
        var childrenAccessAuthorizes = accessAuthorizes.Where(x => x.Parent == parent.Code);
        if (childrenAccessAuthorizes?.Any() ?? false)
        {
            foreach (var accessAuthorize in childrenAccessAuthorizes)
            {
                _logger.LogInformation($"Authorization Service generate resource from AccessAuthorize code: {accessAuthorize.Code}, name: " + accessAuthorize.Name);
                var resource = GetAccessAuthorizeResource(accessAuthorize, parent, roleAdministrator.Id);
                resources.Add(resource);

                InitialAuthorizeTypeAccessRules(accessRules, accessAuthorize, resource, userAnonymous, userSystem, roleAuthenticator);
                await InitialAccessAuthorizeAccessRules(accessRules, accessAuthorize, resource);

                var childrenResources = await InitialResourcesAsync(accessAuthorizes, accessRules, resource, userAnonymous, userSystem, roleAuthenticator, roleAdministrator);
                if (childrenResources.Count != 0)
                {
                    resources.AddRange(childrenResources);
                }
            }
        }

        return resources;
    }
    /// <summary>
    /// 获取系统资源访问权限配置
    /// </summary>
    /// <param name="assembly"></param>
    /// <param name="system"></param>
    /// <returns></returns>
    private List<AccessAuthorizeAttribute>? GetAccessAuthorizes(Assembly assembly, Resource system)
    {
        _logger.LogInformation("Authorization Service will get AccessAuthorize from Assembly: " + assembly.FullName);
        var types = assembly.GetTypes().Where(x => x.IsClass && x.IsPublic && x.HasAttribute<AccessAuthorizeAttribute>() && x.IsFromType<ControllerBase>());
        if (types is null || !types.Any())
        {
            return default;
        }

        var errors = new List<string>();
        var accessAuthorizes = new List<AccessAuthorizeAttribute>();
        foreach (var type in types)
        {
            var controller = GetFromController(type, system, errors);
            if (controller is not null)
            {
                accessAuthorizes.Add(controller);
                var methods = type.GetMethods().Where(x => x.IsPublic && x.HasAttribute<AccessAuthorizeAttribute>());
                foreach (var method in methods)
                {
                    var action = GetFromAction(method, controller, type, errors);
                    if (action is not null)
                    {
                        accessAuthorizes.Add(action);
                        if (action.HttpMethod == Core.HttpMethod.Get && action.Code.EndsWith("Get") && IsODataEndpoint(method))
                        {
                            //如果是 EnableQueryAttribute 特性, 则在添加一条 Get 记录
                            var queryAction = action.Copy();
                            ToODataAccessAuthorize(queryAction);
                            accessAuthorizes.Add(queryAction);
                        }
                    }
                }
            }
        }

        if (errors.Count != 0)
        {
            _logger.LogWarning("Authorization Service get AccessAuthorize from Assembly: " + assembly.FullName + " error AccessAuthorizes: " + string.Join(CHAR_COMMA, errors));
            return default;
        }

        return accessAuthorizes;
    }
    /// <summary>
    /// 从 controller 获取设置的 AccessAuthorizeAttribute 特性
    /// </summary>
    /// <param name="type"></param>
    /// <param name="system"></param>
    /// <param name="errors"></param>
    /// <returns></returns>
    private AccessAuthorizeAttribute? GetFromController(Type type, Resource system, List<string> errors)
    {
        _logger.LogInformation($"Authorization Service will get AccessAuthorize from Controller: {type.FullName}");
        var controller = type.GetCustomAttribute<AccessAuthorizeAttribute>();
        if (controller is null || string.IsNullOrWhiteSpace(controller.Code))
        {
            _logger.LogWarning($"Authorization Service get AccessAuthorize from Controller: {type.FullName} error!");
            errors.Add(type.FullName ?? type.Name);
            return null;
        }

        if (controller.Code.Length > 36)
        {
            _logger.LogWarning($"Authorization Service get AccessAuthorize from Controller: {type.FullName} error, code too long!");
            errors.Add(type.FullName ?? type.Name);
            return null;
        }

        controller.System ??= system.Id;
        controller.Parent ??= system.Code;
        controller.Title ??= controller.Name ?? type.GetDisplayName();
        controller.Name = system.Name + CHAR_HLINE + controller.Title;
        if (string.IsNullOrWhiteSpace(controller.Content))
        {
            controller.Content = type.GetControllerRoutePath();
        }

        if (controller.ResourceType == ResourceType.Api)
        {
            controller.ResourceType = type.IsFromType<Controller>() ? ResourceType.Module : ResourceType.Service;
        }

        if (controller.AuthorizeType == AuthorizeType.Internal && type.HasAttribute<AllowAnonymousAttribute>())
        {
            controller.AuthorizeType = AuthorizeType.Anonymous;
        }

        return controller;
    }
    /// <summary>
    /// 从 action 获取设置的 AccessAuthorizeAttribute 特性
    /// </summary>
    /// <param name="method"></param>
    /// <param name="controller"></param>
    /// <param name="type"></param>
    /// <param name="errors"></param>
    /// <returns></returns>
    private AccessAuthorizeAttribute? GetFromAction(MethodInfo method, AccessAuthorizeAttribute controller, Type type, List<string> errors)
    {
        _logger.LogInformation("Authorization Service will get AccessAuthorize from Action: " + method.Name);
        var action = method.GetCustomAttribute<AccessAuthorizeAttribute>();
        if (action is null || string.IsNullOrWhiteSpace(action.Code))
        {
            _logger.LogWarning($"Authorization Service get AccessAuthorize from Controller: {controller.Name} Action: {method.Name} error, code is empty!");
            errors.Add(method.Name);
            return null;
        }

        if (action.Code.Length > 36)
        {
            _logger.LogWarning($"Authorization Service get AccessAuthorize from Controller: {controller.Name} Action: {method.Name} error, code too long!");
            errors.Add(method.Name);
            return null;
        }

        if (method.DeclaringType != type)
        {
            //来自基类的方法申明, Code 需要合成
            action.Code = controller.Code + action.Code;
        }

        action.System ??= controller.System;
        action.Parent ??= controller.Code;
        action.Title ??= action.Name ?? method.GetDisplayName();
        action.Name = controller.Name + CHAR_HLINE + action.Title;
        action.Users ??= controller.Users;
        action.Roles ??= controller.Roles;
        action.Groups ??= controller.Groups;
        action.Policies ??= controller.Policies;
        action.Condition ??= controller.Condition;
        action.HttpMethod ??= method.GetCustomAttributes<HttpMethodAttribute>()?.GetHttpMethod();

        if (string.IsNullOrWhiteSpace(action.Content))
        {
            action.Content = method.GetActionRoutePath();
        }

        if (action.AuthorizeType == AuthorizeType.Internal && method.HasAttribute<AllowAnonymousAttribute>())
        {
            action.AuthorizeType = AuthorizeType.Anonymous;
        }

        if (action.ResourceType == ResourceType.Page && action.HttpMethod is null)
        {
            action.HttpMethod = Core.HttpMethod.Get;
        }

        if (string.IsNullOrWhiteSpace(action.Content))
        {
            _logger.LogWarning($"Authorization Service get AccessAuthorize from Controller: {controller.Name} Action: {method.Name} error, no content error!");
            errors.Add(method.Name);
            return null;
        }

        return action;
    }
    /// <summary>
    /// 从 AccessAuthorizeAttribute 获取资源
    /// </summary>
    /// <param name="accessAuthorize"></param>
    /// <param name="parent"></param>
    /// <param name="roleAdministratorId"></param>
    /// <returns></returns>
    private Resource GetAccessAuthorizeResource(AccessAuthorizeAttribute accessAuthorize, Resource parent, string roleAdministratorId)
    {
        var resource = new Resource
        {
            SystemId = parent.SystemId ?? parent.Id,
            ParentId = parent.Id,
            Code = accessAuthorize.Code,
            Name = accessAuthorize.Name,
            Alias = accessAuthorize.Title ?? accessAuthorize.Name,
            Title = accessAuthorize.Title!,
            Content = accessAuthorize.Content ?? accessAuthorize.Code,
            ResourceType = accessAuthorize.ResourceType,
            Type = accessAuthorize.AuthorizeType,
            AccessLevel = AccessLevel.Internal,
            OwnerType = IdentityType.Role,
            OwnerId = roleAdministratorId,
            Description = accessAuthorize.Name,
            HttpMethod = accessAuthorize.HttpMethod,
            Display = accessAuthorize.Display
        };

        resource.Parent = parent;
        resource.Create(USER_SYSTEM);
        resource.Update(USER_SYSTEM);
        return resource;
    }
    /// <summary>
    /// 写入前先删除已存在的资源和访问规则
    /// </summary>
    /// <param name="system">要删除的系统资源</param>
    /// <returns></returns>
    private async Task RemoveExistAsync(Resource system)
    {
        var existSystem = await _context.Resources.FirstOrDefaultAsync(x => x.Code == system.Code);
        if (existSystem is not null)
        {
            var resources = await _context.Resources.Where(x => x.Id == existSystem.Id || x.SystemId == existSystem.Id).ToListAsync();
            var accessRules = await _context.AccessRules.Where(x => resources.Select(a => a.Id).Contains(x.ResourceId)).ToListAsync();
            if (accessRules.Count > 0)
            {
                _context.AccessRules.RemoveRange(accessRules);
            }

            _context.Resources.RemoveRange(resources);
            await _context.SaveChangesAsync();
        }
    }
    /// <summary>
    /// 判断一个终结点是否 odata 方法
    /// </summary>
    /// <param name="methodInfo"></param>
    /// <returns></returns>
    private bool IsODataEndpoint(MethodInfo methodInfo) => methodInfo.GetCustomAttributes().Any(x => x.GetType().Name == "EnableQueryAttribute");
    /// <summary>
    /// 将 api accessAuthorize 转换为 odata accessAuthorize
    /// </summary>
    /// <param name="accessAuthorize"></param>
    /// <returns></returns>
    private AccessAuthorizeAttribute ToODataAccessAuthorize(AccessAuthorizeAttribute accessAuthorize)
    {
        accessAuthorize.Code = accessAuthorize.Code.Replace("Get", "ODataGet");
        accessAuthorize.Content = accessAuthorize.Content?.Replace("api/", "odata/");
        return accessAuthorize;
    }
    #endregion

    #region 访问规则初始化
    /// <summary>
    /// 初始化 AuthorizeType 类型规则
    /// </summary>
    /// <param name="accessRules"></param>
    /// <param name="accessAuthorize"></param>
    /// <param name="resource"></param>
    /// <param name="userAnonymous"></param>
    /// <param name="userSystem"></param>
    /// <param name="roleAuthenticator"></param>
    private void InitialAuthorizeTypeAccessRules(List<AccessRule> accessRules, AccessAuthorizeAttribute accessAuthorize, Resource resource, User userAnonymous, User userSystem, Role roleAuthenticator)
    {
        //匿名角色
        if (accessAuthorize.AuthorizeType == AuthorizeType.Anonymous)
        {
            AccessRuleAllowUser(accessRules, accessAuthorize, resource, userAnonymous);
        }

        //系统角色
        if (accessAuthorize.AuthorizeType == AuthorizeType.System)
        {
            AccessRuleAllowUser(accessRules, accessAuthorize, resource, userSystem);
        }

        //认证用户角色
        if (accessAuthorize.AuthorizeType == AuthorizeType.Authenticator)
        {
            AccessRuleAllowRole(accessRules, accessAuthorize, resource, roleAuthenticator);
        }
    }
    /// <summary>
    /// 初始化 users, roles, groups, levels 配置项
    /// </summary>
    /// <param name="accessRules"></param>
    /// <param name="accessAuthorize"></param>
    /// <param name="resource"></param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    private async Task InitialAccessAuthorizeAccessRules(List<AccessRule> accessRules, AccessAuthorizeAttribute accessAuthorize, Resource resource)
    {
        if (!string.IsNullOrWhiteSpace(accessAuthorize.Users))
        {
            var names = accessAuthorize.Users!.Split(CHAR_COMMA);
            if (names.Length != 0)
            {
                var users = await _context.Users.Where(x => names.Contains(x.UserName)).ToListAsync();
                if (users is null || !users.Any())
                {
                    throw new Exception($"AccessAuthorize code: {accessAuthorize.Code}, name: {accessAuthorize.Name} config users: {accessAuthorize.Users} not exist!");
                }

                foreach (var user in users)
                {
                    AccessRuleAllowUser(accessRules, accessAuthorize, resource, user);
                }
            }
        }

        if (!string.IsNullOrWhiteSpace(accessAuthorize.Roles))
        {
            var names = accessAuthorize.Roles!.Split(CHAR_COMMA);
            if (names.Length != 0)
            {
                var roles = await _context.Roles.Where(x => names.Contains(x.Name)).ToListAsync();
                if (roles is null || !roles.Any())
                {
                    throw new Exception($"AccessAuthorize code: {accessAuthorize.Code}, name: {accessAuthorize.Name} config roles: {accessAuthorize.Roles} not exist!");
                }

                foreach (var role in roles)
                {
                    AccessRuleAllowRole(accessRules, accessAuthorize, resource, role);
                }
            }
        }

        if (!string.IsNullOrWhiteSpace(accessAuthorize.Groups))
        {
            var names = accessAuthorize.Groups!.Split(CHAR_COMMA);
            if (names.Length != 0)
            {
                var groups = await _context.Groups.Where(x => names.Contains(x.Name)).ToListAsync();
                if (groups is null || !groups.Any())
                {
                    throw new Exception($"AccessAuthorize code: {accessAuthorize.Code}, name: {accessAuthorize.Name} config groups: {accessAuthorize.Groups} not exist!");
                }

                foreach (var group in groups)
                {
                    AccessRuleAllowGroup(accessRules, accessAuthorize, resource, group);
                }
            }
        }

        if (!string.IsNullOrWhiteSpace(accessAuthorize.Levels))
        {
            var names = accessAuthorize.Levels!.Split(CHAR_COMMA);
            if (names.Length != 0)
            {
                var levels = await _context.Levels.Where(x => names.Contains(x.Name)).ToListAsync();
                if (levels is null || !levels.Any())
                {
                    throw new Exception($"AccessAuthorize code: {accessAuthorize.Code}, name: {accessAuthorize.Name} config levels: {accessAuthorize.Levels} not exist!");
                }

                foreach (var level in levels)
                {
                    AccessRuleAllowLevel(accessRules, accessAuthorize, resource, level);
                }
            }
        }
    }

    /// <summary>
    /// 设置系统级别先禁止访问
    /// </summary>
    /// <param name="system"></param>
    /// <returns></returns>
    private AccessRule AccessRuleSystemForbid(Resource system)
    {
        var accessRule = new AccessRule
        {
            ResourceId = system.Id,
            IdentityType = IdentityType.All,
            Code = nameof(IdentityType.All) + CHAR_AT + system.Code,
            IsAllow = AllowType.Forbid,
            Priority = UNIT_ZERO
        };

        accessRule.Create(USER_SYSTEM);
        accessRule.Update(USER_SYSTEM);
        return accessRule;
    }
    /// <summary>
    /// 设置系统级别允许部分角色访问
    /// </summary>
    /// <param name="system"></param>
    /// <param name="role"></param>
    /// <returns></returns>
    private AccessRule AccessRuleSystemAllow(Resource system, Role role)
    {
        var accessRule = new AccessRule
        {
            ResourceId = system.Id,
            IdentityType = IdentityType.Role,
            IdentityId = role.Id,
            Code = role.Name + CHAR_AT + system.Code,
            IsAllow = AllowType.Allow,
            Priority = GetPriorityByAuthorizeType(role.Type)
        };

        accessRule.Create(USER_SYSTEM);
        accessRule.Update(USER_SYSTEM);
        return accessRule;
    }

    /// <summary>
    /// 设置用户可访问
    /// code: userName#resource.code
    /// </summary>
    /// <param name="accessRules"></param>
    /// <param name="accessAuthorize"></param>
    /// <param name="resource"></param>
    /// <param name="user"></param>
    /// <returns></returns>
    private void AccessRuleAllowUser(List<AccessRule> accessRules, AccessAuthorizeAttribute accessAuthorize, Resource resource, User user)
    {
        //已存在当前规则
        var code = user.UserName + CHAR_SHARP + resource.Code;
        if (accessRules.Any(x => x.Code == code))
        {
            return;
        }

        //父项规则存在且规则大于等于当前规则, 则不在创建当前规则
        var priority = GetPriorityByAuthorizeType(user.Type);
        if ((resource.Parent is not null && accessRules.Any(x => x.Code == (user.Name + CHAR_AT + resource.Parent.Code) && x.IsAllow == AllowType.Allow && x.Priority >= priority)))
        {
            return;
        }

        var accessRule = new AccessRule
        {
            ResourceId = resource.Id,
            IdentityType = GetIdentityTypeByAuthorizeType(accessAuthorize.AuthorizeType),
            IdentityId = user.Id,
            Code = code,
            Condition = accessAuthorize.Condition,
            IsAllow = AllowType.Allow,
            Priority = priority
        };

        accessRule.Create(USER_SYSTEM);
        accessRule.Update(USER_SYSTEM);
        accessRules.Add(accessRule);
    }
    /// <summary>
    /// 设置角色可访问
    /// code: name@resource.code
    /// </summary>
    /// <param name="accessRules"></param>
    /// <param name="accessAuthorize"></param>
    /// <param name="resource"></param>
    /// <param name="role"></param>
    /// <returns></returns>
    private void AccessRuleAllowRole(List<AccessRule> accessRules, AccessAuthorizeAttribute accessAuthorize, Resource resource, Role role)
    {
        //以下角色初始化时不在直接分配, 而是由 AuthorizeType 类型分配
        if (role.Type == AuthorizeType.Administrator || role.Type == AuthorizeType.Developer || role.Type == AuthorizeType.Anonymous || role.Type == AuthorizeType.System)
        {
            return;
        }

        //已存在当前规则
        var code = role.Name + CHAR_AT + resource.Code;
        if (accessRules.Any(x => x.Code == code))
        {
            return;
        }

        //父项规则存在且规则大于等于当前规则, 则不在创建当前规则
        var priority = GetPriorityByAuthorizeType(role.Type);
        if ((resource.Parent is not null && accessRules.Any(x => x.Code == (role.Name + CHAR_AT + resource.Parent.Code) && x.IsAllow == AllowType.Allow && x.Priority >= priority)))
        {
            return;
        }

        //父项规则存在且规则大于等于当前规则, 则不在创建当前规则
        if ((resource.Parent is not null && resource.Parent.Parent is not null && accessRules.Any(x => x.Code == (role.Name + CHAR_AT + resource.Parent.Parent.Code) && x.IsAllow == AllowType.Allow && x.Priority >= priority)))
        {
            return;
        }

        var accessRule = new AccessRule
        {
            ResourceId = resource.Id,
            IdentityType = IdentityType.Role,
            IdentityId = role.Id,
            Code = code,
            Condition = accessAuthorize.Condition,
            IsAllow = AllowType.Allow,
            Priority = priority
        };

        accessRule.Create(USER_SYSTEM);
        accessRule.Update(USER_SYSTEM);
        accessRules.Add(accessRule);
    }
    /// <summary>
    /// 设置组织单位可访问
    /// code: name+resource.code
    /// </summary>
    /// <param name="accessRules"></param>
    /// <param name="accessAuthorize"></param>
    /// <param name="resource"></param>
    /// <param name="group"></param>
    /// <returns></returns>
    private void AccessRuleAllowGroup(List<AccessRule> accessRules, AccessAuthorizeAttribute accessAuthorize, Resource resource, Group group)
    {
        //已存在当前规则
        var code = group.Name + CHAR_AT + resource.Code;
        if (accessRules.Any(x => x.Code == code))
        {
            return;
        }

        //父项规则存在且规则大于等于当前规则, 则不在创建当前规则
        var priority = GetPriorityByAuthorizeType(group.Type);
        if ((resource.Parent is not null && accessRules.Any(x => x.Code == (group.Name + CHAR_AT + resource.Parent.Code) && x.IsAllow == AllowType.Allow && x.Priority >= priority)))
        {
            return;
        }

        var accessRule = new AccessRule
        {
            ResourceId = resource.Id,
            IdentityType = IdentityType.Group,
            IdentityId = group.Id,
            Code = code,
            Condition = accessAuthorize.Condition,
            IsAllow = AllowType.Allow,
            Priority = priority
        };

        accessRule.Create(USER_SYSTEM);
        accessRule.Update(USER_SYSTEM);
        accessRules.Add(accessRule);
    }
    /// <summary>
    /// 设置级别可访问
    /// code: name-resource.code
    /// </summary>
    /// <param name="accessRules"></param>
    /// <param name="accessAuthorize"></param>
    /// <param name="resource"></param>
    /// <param name="level"></param>
    /// <returns></returns>
    private void AccessRuleAllowLevel(List<AccessRule> accessRules, AccessAuthorizeAttribute accessAuthorize, Resource resource, Level level)
    {
        //已存在当前规则
        var code = level.Name + CHAR_AT + resource.Code;
        if (accessRules.Any(x => x.Code == code))
        {
            return;
        }

        //父项规则存在且规则大于等于当前规则, 则不在创建当前规则
        var priority = GetPriorityByAuthorizeType(level.Type);
        if ((resource.Parent is not null && accessRules.Any(x => x.Code == (level.Name + CHAR_AT + resource.Parent.Code) && x.IsAllow == AllowType.Allow && x.Priority >= priority)))
        {
            return;
        }

        var accessRule = new AccessRule
        {
            ResourceId = resource.Id,
            IdentityType = IdentityType.Level,
            IdentityId = level.Id,
            Code = code,
            Condition = accessAuthorize.Condition,
            IsAllow = AllowType.Allow,
            Priority = priority
        };

        accessRule.Create(USER_SYSTEM);
        accessRule.Update(USER_SYSTEM);
        accessRules.Add(accessRule);
    }

    /// <summary>
    /// 根据授权类型获取设定优先级级别
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    private int GetPriorityByAuthorizeType(AuthorizeType type) => type switch
    {
        AuthorizeType.Anonymous => 1,
        AuthorizeType.Public => 2,
        AuthorizeType.Authenticator => 9,
        AuthorizeType.AuthorizeAccessor => 19,
        AuthorizeType.ThirdParty or AuthorizeType.Partner => 29,
        AuthorizeType.Protected => 39,
        AuthorizeType.ExternalAdministrator => 49,
        AuthorizeType.Internal or AuthorizeType.Official => 69,
        AuthorizeType.Authorizer => 79,
        AuthorizeType.Administrator or AuthorizeType.Developer => 89,
        AuthorizeType.System => 99,
        _ => 0
    };
    /// <summary>
    /// 从 AuthorizeType 类型推断 OwnerType 类型
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    private IdentityType GetIdentityTypeByAuthorizeType(AuthorizeType type) => type switch
    {
        AuthorizeType.Anonymous => IdentityType.Anonymous,
        AuthorizeType.System => IdentityType.System,
        _ => IdentityType.User
    };
    #endregion

    #region 授权相关内部方法
    /// <summary>
    /// 从 HttpContext 获取当前访问的接口 AccessAuthorize 信息
    /// </summary>
    /// <returns></returns>
    private string? GetAccessAuthorizeCode()
    {
        var endpoint = _httpContext.GetEndpoint();
        if (endpoint is not null)
        {
            var metadata = endpoint.Metadata.FirstOrDefault(x => x.GetType() == typeof(ControllerActionDescriptor));
            if (metadata is not null && metadata is ControllerActionDescriptor descriptor)
            {
                var methodInfo = descriptor.MethodInfo;
                var accessAuthorize = methodInfo.GetCustomAttribute<AccessAuthorizeAttribute>();
                if (accessAuthorize is not null)
                {
                    if (methodInfo.DeclaringType != descriptor.ControllerTypeInfo)
                    {
                        var controller = descriptor.ControllerTypeInfo.GetCustomAttribute<AccessAuthorizeAttribute>();
                        if (controller is not null)
                        {
                            return controller.Code + accessAuthorize.Code;
                        }
                    }

                    if (IsODataEndpoint(methodInfo))
                    {
                        ToODataAccessAuthorize(accessAuthorize);
                        return accessAuthorize.Code;
                    }

                    return accessAuthorize.Code;
                }
            }
        }

        return null;
    }
    /// <summary>
    /// 计算用户授权数据并写入缓存
    /// </summary>
    /// <param name="userContext"></param>
    /// <returns></returns>
    private async Task WriteToCacheAsync(UserContext userContext)
    {
        _logger.LogInformation($"{_userAccess}: will calculate and set user access resources to cache!");
        foreach (var resource in userContext.Resources)
        {
            var authorizationModel = new UserAuthorizationContext
            {
                UserId = userContext.UserId,
                UserName = userContext.UserName,
                UserState = userContext.User.State,
                ResourceId = resource.Id,
                Code = resource.Code,
                Path = resource.Content,
                AccessCount = resource.AccessRules?.Min(x => x.AccessCount),
                ExpireTime = resource.AccessRules?.Min(x => x.ExpireTime),
                HasAccessCondition = resource.AccessRules?.Any(x => !string.IsNullOrWhiteSpace(x.Condition)) ?? false,
                HttpMethod = resource.HttpMethod,
                IsAuthorized = true,
                IsAuthorizeAccess = resource.AccessRules?.Any(x => x.IsAllow == AllowType.Authorize) ?? false
            };

            if (authorizationModel.AccessCount == 0)
            {
                authorizationModel.AccessCount = null;
            }

            if (authorizationModel.ExpireTime == DateTime.MinValue)
            {
                authorizationModel.ExpireTime = null;
            }

            await _cache.SetValueAsync(authorizationModel.GetCacheKey(), authorizationModel, _cacheEntryOptions);
        }

        _logger.LogInformation($"{_userAccess}: calculate and set user access resources to cache finished!");
    }
    /// <summary>
    /// 获取用户访问规则及可访问资源
    /// </summary>
    /// <param name="userContext"></param>
    /// <returns></returns>
    private async Task GetUserAccessResources(UserContext userContext)
    {
        _logger.LogInformation($"{_userAccess}: get user authorized access resources started!");
        var systemResources = await GetAllSystemResourceTreeAsync();
        var accessRules = await GetUserAccessRulesAsync(userContext);
        var accessResources = GetSystemAccessResources(systemResources, accessRules);
        var resourcesTrees = GetAccessResourceTrees(systemResources, accessResources);

        userContext.Resources.Clear();
        userContext.ResourceTrees.Clear();
        userContext.Resources.AddRange(accessResources);
        userContext.ResourceTrees.AddRange(resourcesTrees);

        await WriteToCacheAsync(userContext);
        _logger.LogInformation($"{_userAccess}: get user authorized access resources finished!");
    }

    /// <summary>
    /// 获取并构造系统全部的资源树
    /// </summary>
    /// <returns></returns>
    private async Task<List<ResourceViewModel>> GetAllSystemResourceTreeAsync()
    {
        _logger.LogInformation($"{_userAccess}: will get all resources!");
        var resources = await _context.Resources.AsNoTracking()
            .Where(x => x.State == DataState.Available)
            .OrderBy(x => x.Code)
            .Select(x => new ResourceViewModel
            {
                Id = x.Id,
                Name = x.Name,
                Code = x.Code,
                IdentityType = x.OwnerType,
                OwnerId = x.OwnerId,
                AccessLevel = x.AccessLevel,
                Alias = x.Alias,
                Content = x.Content,
                HttpMethod = x.HttpMethod,
                Display = x.Display,
                Image = x.Image,
                LevelId = x.LevelId,
                ParentId = x.ParentId,
                ResourceType = x.ResourceType,
                State = x.State,
                SystemId = x.SystemId,
                Title = x.Title,
                Type = x.Type
            })
            .ToListAsync();
        _logger.LogInformation($"{_userAccess}: has been got all resources: {resources.Count}");

        if (resources.Count == 0)
        {
            throw new Exception("system resources have not initialized yet!");
        }

        var systems = resources.Where(x => x.ResourceType == ResourceType.System && x.SystemId == null).ToList().Copy();
        if (systems.Count > 0)
        {
            //构造资源树
            _logger.LogInformation($"{_userAccess}: will generate system resources trees for: {systems.Count} system");
            foreach (var resource in systems)
            {
                GetChildrenResources(resource, resources.Where(x => x.SystemId == resource.Id));
            }

            _logger.LogInformation($"{_userAccess}: has been generated system resources trees for: {systems.Count} system");
        }

        return systems;
    }
    /// <summary>
    /// 获取用户所有已分配的访问规则列表
    /// </summary>
    /// <param name="userContext">认证上下文</param>
    /// <returns></returns>
    private async Task<ICollection<AccessRuleViewModel>?> GetUserAccessRulesAsync(UserContext userContext)
    {
        _logger.LogInformation($"{_userAccess}: get user {userContext.UserName} identities from UserContext!");
        var identities = new List<string>();
        if (userContext.UserId is not null)
        {
            identities.Add(userContext.UserId);
        }

        if (userContext.Group is not null && (!(userContext.Identities?.Any(x => x.Key == IdentityType.Group) ?? false) || userContext.Identities[IdentityType.Group].Contains(userContext.Group.Name)))
        {
            identities.Add(userContext.Group.Id);
        }

        if (userContext.Level is not null)
        {
            identities.Add(userContext.Level.Id);
        }

        if (userContext.Roles is not null && userContext.Roles.Count != 0)
        {
            if (userContext.Identities?.Any(x => x.Key == IdentityType.Role) ?? false)
            {
                identities.AddRange(userContext.Roles.Where(x => x.State == DataState.Available && userContext.Identities[IdentityType.Role].Contains(x.Name)).Select(x => x.Id));
            }
            else
            {
                identities.AddRange(userContext.Roles.Select(x => x.Id));
            }
        }

        if (userContext.Groups is not null && userContext.Groups.Count != 0)
        {
            if (userContext.Identities?.Any(x => x.Key == IdentityType.Group) ?? false)
            {
                identities.AddRange(userContext.Groups.Where(x => x.State == DataState.Available && userContext.Identities[IdentityType.Group].Contains(x.Name)).Select(x => x.Id));
            }
            else
            {
                identities.AddRange(userContext.Groups.Select(x => x.Id));
            }
        }

        //获取用户系统身份
        if (userContext.IsSystem)
        {
            //系统中只能有一个 System 类型的用户的用户名叫: USER_SYSTEM 的
            var systemUser = await _context.Users.AsNoTracking().FirstOrDefaultAsync(x => x.State == DataState.Available && x.Type == AuthorizeType.System && x.UserName == USER_SYSTEM);
            if (systemUser is not null)
            {
                identities.Add(systemUser.Id);
            }
        }

        //获取匿名用户身份
        //系统中只能有一个 Anonymous 类型的用户, 且用户名叫: USER_ANONYMOUS 的
        var anonymousUser = await _context.Users.AsNoTracking().FirstOrDefaultAsync(x => x.State == DataState.Available && x.Type == AuthorizeType.Anonymous && x.UserName == USER_ANONYMOUS);
        if (anonymousUser is not null)
        {
            identities.Add(anonymousUser.Id);
        }

        //查询满足条件的所有访问规则
        _logger.LogInformation($"{_userAccess}: get user {userContext.UserName} access rules from database!");
        var accessRules = await _context.AccessRules.AsNoTracking()
            .Where(x => x.State == DataState.Available && (!x.ExpireTime.HasValue || x.ExpireTime.Value >= DateTime.UtcNow) && ((x.IdentityId != null && identities.Contains(x.IdentityId)) || x.IdentityType == IdentityType.All))
            .Select(x => new AccessRuleViewModel
            {
                Id = x.Id,
                AccessCount = x.AccessCount,
                Code = x.Code,
                Condition = x.Condition,
                ExpireTime = x.ExpireTime,
                IdentityId = x.IdentityId,
                IdentityType = x.IdentityType,
                IsAllow = x.IsAllow,
                Priority = x.Priority,
                ResourceId = x.ResourceId,
                State = x.State
            })
            .ToListAsync();

        _logger.LogInformation($"{_userAccess}: get user {userContext.UserName} access rules: {accessRules.Count}");
        return accessRules;
    }
    /// <summary>
    /// 获取指定身份的已分配访问规则列表
    /// </summary>
    /// <param name="identityType">身份类型</param>
    /// <param name="identityId">身份编号</param>
    /// <returns></returns>
    private async Task<ICollection<AccessRuleViewModel>?> GetIdentityAccessRulesAsync(IdentityType identityType, string identityId)
    {
        _logger.LogInformation($"{_userAccess}: will get identity {identityType} id {identityId} access rules from database!");
        var accessRules = await _context.AccessRules.AsNoTracking()
        .Where(x => x.State == DataState.Available && (!x.ExpireTime.HasValue || x.ExpireTime.Value >= DateTime.UtcNow) && ((x.IdentityType == identityType && x.IdentityId == identityId) || x.IdentityType == IdentityType.All))
        .Select(x => new AccessRuleViewModel
        {
            Id = x.Id,
            AccessCount = x.AccessCount,
            Code = x.Code,
            Condition = x.Condition,
            ExpireTime = x.ExpireTime,
            IdentityId = x.IdentityId,
            IdentityType = x.IdentityType,
            IsAllow = x.IsAllow,
            Priority = x.Priority,
            ResourceId = x.ResourceId,
            State = x.State
        })
        .ToListAsync();

        _logger.LogInformation($"{_userAccess}: will get identity {identityType} id {identityId} access rules: {accessRules.Count}");
        return accessRules;
    }
    /// <summary>
    /// 从全量的系统资源树中过滤出当前可访问的资源树
    /// </summary>
    /// <param name="systemResources"></param>
    /// <param name="accessResources"></param>
    /// <returns></returns>
    private ICollection<ResourceViewModel> GetAccessResourceTrees(ICollection<ResourceViewModel> systemResources, ICollection<ResourceViewModel> accessResources)
    {
        _logger.LogInformation($"{_userAccess}: calculate user access resource trees!");
        var resourcesTrees = new List<ResourceViewModel>();
        foreach (var resource in systemResources)
        {
            CutAccessResourceTree(accessResources, resource);
            if (resource.Children is not null && resource.Children.Count != 0)
            {
                resourcesTrees.Add(resource);
            }
        }

        return resourcesTrees;
    }
    /// <summary>
    /// 根据访问规则计算可访问资源列表
    /// </summary>
    /// <param name="systems">系统资源树</param>
    /// <param name="accessRules">访问规则</param>
    /// <returns></returns>
    private ICollection<ResourceViewModel> GetSystemAccessResources(ICollection<ResourceViewModel> systems, ICollection<AccessRuleViewModel>? accessRules)
    {
        var accessResources = new List<ResourceViewModel>();
        if (accessRules is not null && accessRules.Count != 0)
        {
            _logger.LogInformation($"{_userAccess}: calculate user access resources which can be accessed!");
            foreach (var resource in systems)
            {
                accessResources.AddRange(GetAccessResources(resource, [], accessRules));
            }
        }

        //可访问资源为非系统级资源
        return accessResources
            .Where(x => x.SystemId != null && x.ResourceType < ResourceType.System)
            .OrderBy(x => x.Code)
            .ThenBy(x => x.Content)
            .ToList();
    }
    /// <summary>
    /// 对权限树中的当前节点及其子节点设置权限
    /// </summary>
    /// <param name="resource">当前资源节点</param>
    /// <param name="accessRules">当前节点拥有的访问规则列表</param>
    /// <param name="fullAccessRules">当前用户获取到的所有访问规则列表</param>
    private ICollection<ResourceViewModel> GetAccessResources(ResourceViewModel resource, List<AccessRuleViewModel> accessRules, ICollection<AccessRuleViewModel> fullAccessRules)
    {
        _logger.LogDebug($"{_userAccess}: get authorized access from resource: {resource.Code} children nodes!");
        var resources = new List<ResourceViewModel>();

        //1. 先获取当前资源满足条件的访问规则
        var currentAccessRules = fullAccessRules.Where(x => x.ResourceId == resource.Id).UnionBy(accessRules, x => x.Id).OrderByDescending(x => x.Priority).ToList();

        //2. 如果当前资源有子资源
        if (resource.Children is not null && resource.Children.Count != 0)
        {
            //3. 且当前资源有权访问, 则递归设置子资源的权限
            foreach (var child in resource.Children)
            {
                resources.AddRange(GetAccessResources(child, currentAccessRules, fullAccessRules));
            }
        }

        //3. 按照规则优先级最高的一条设置当前资源的权限, 如果当前资源没有访问规则，则视为无权限; 或当前资源的子资源有访问权限, 则认为当前资源有访问权限
        var hasPermission = currentAccessRules.FirstOrDefault()?.IsAllow == AllowType.Allow;
        if (hasPermission || resources.Count != 0)
        {
            resources.Add(new ResourceViewModel
            {
                Id = resource.Id,
                State = resource.State,
                Name = resource.Name,
                Code = resource.Code,
                Alias = resource.Alias,
                Type = resource.Type,
                Image = resource.Image,
                Title = resource.Title,
                Content = resource.Content,
                ParentId = resource.ParentId,
                SystemId = resource.SystemId,
                Description = resource.Description,
                ResourceType = resource.ResourceType,
                IdentityType = resource.IdentityType,
                OwnerId = resource.OwnerId,
                LevelId = resource.LevelId,
                HttpMethod = resource.HttpMethod,
                Display = resource.Display,
                AccessLevel = resource.AccessLevel,
                HasPermission = hasPermission,
                //TODO 记录当前资源的访问规则会增加系统处理量, 但使用率不高
                AccessRules = currentAccessRules
            });
        }

        _logger.LogDebug($"{_userAccess}: get authorized access from resource node: {resource.Code} children nodes: {resources.Count}");
        return resources.OrderBy(x => x.Code).ToList();
    }
    /// <summary>
    /// 递归获取并设置资源的子资源
    /// </summary>
    /// <param name="resource"></param>
    /// <param name="resources"></param>
    private static void GetChildrenResources(ResourceViewModel resource, IEnumerable<ResourceViewModel> resources)
    {
        var children = resources.Where(x => x.ParentId == resource.Id).OrderBy(x => x.Code).ToList();
        if (children is not null && children.Count != 0)
        {
            resource.Children = children;
            foreach (var child in children)
            {
                GetChildrenResources(child, resources);
            }
        }
    }
    /// <summary>
    /// 对可访问的资源树进行剪枝操作
    /// </summary>
    /// <param name="source">有权限的所有资源原数据</param>
    /// <param name="target">当前权限节点</param>
    /// <returns></returns>
    private static void CutAccessResourceTree(ICollection<ResourceViewModel> source, ResourceViewModel target)
    {
        target.Children = [];
        var children = source.Where(x => x.ParentId == target.Id).ToList().Copy();
        if (children.Count != 0)
        {
            foreach (var child in children)
            {
                target.Children.Add(child);
                CutAccessResourceTree(source, child);
            }
        }
    }
    /// <summary>
    /// 访问规则校验
    /// 验证并记录访问次数
    /// 
    /// 验证访问规则条件, 条件授权需要全部满足, 不像 IsAllow 只需最高优先级的满足即可
    /// 目前支持的规则包括两种
    /// 1. @method params..., @ 开头的参数为调用内部方法进行访问授权, 方法的第一个参数不需要填写, 均为 UserContext
    /// 2. {field} operator value, 其中 {field} 为路由参数, 用户上下文的参数, 和来自 http 上下文参数
    /// 
    /// </summary>
    /// <param name="userAuthorization">用户授权</param>
    /// <returns></returns>
    private async Task<bool> AuthorizeAccessRuleAsync(UserAuthorizationContext userAuthorization)
    {
        ////同一个资源同一个用户非授权类型只会存在唯一一条记录
        //var accessRecord = await _context.AccessRecords.FirstOrDefaultAsync(x => x.State == DataState.Available && x.UserId == userAuthorization.UserId && x.ResourceId == userAuthorization.ResourceId && x.AccessRuleId == null);
        //if (accessRecord is null && userAuthorization.IsAuthorized && !string.IsNullOrWhiteSpace(userAuthorization.UserId) && !string.IsNullOrWhiteSpace(userAuthorization.ResourceId) && userAuthorization.UserState != DataState.Draft)
        //{
        //    //新增用户对某一资源的访问记录
        //    accessRecord = new AccessRecord
        //    {
        //        UserId = userAuthorization.UserId,
        //        ResourceId = userAuthorization.ResourceId
        //    };

        //    accessRecord.Create();
        //    await _context.AccessRecords.AddAsync(accessRecord);
        //    await _context.SaveChangesAsync();
        //}

        //if (accessRecord is not null)
        //{
        //    accessRecord.AccessCount++;
        //}

        //var result = true;
        //if (viewModel.AccessRules is not null)
        //{
        //    foreach (var accessRule in viewModel.AccessRules)
        //    {
        //        if (accessRule.AccessCount > 0 && accessRecord?.AccessCount > accessRule.AccessCount)
        //        {
        //            //当且仅当访问规则中最小的限制访问次数大于 0, 且访问次数已经大于限制访问次数, 将拒绝访问
        //            _logger.LogInformation($"user {userAuthorization.UserName} access resource {userAuthorization.Code} in access rule {accessRule.Code} has been accessed count {accessRecord.AccessCount} greater than limited count {accessRule.AccessCount}, will be denied!");
        //            result = false;
        //            break;
        //        }

        //        if (!string.IsNullOrWhiteSpace(accessRule.Condition) && accessRule.Code is not null)
        //        {
        //            var conditions = accessRule.Condition.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
        //            for (int index = 0; index < conditions.Length; index++)
        //            {
        //                var condition = conditions[index];
        //                var values = condition.Split(CHAR_SPACE, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
        //                var field = values[0];
        //                if (field.StartsWith(CHAR_AT))
        //                {
        //                    result = MatchMethod(userContext, accessRule.Code, condition, values);
        //                }
        //                else if (values.Length >= 3 && field.StartsWith('{') && field.EndsWith('}'))
        //                {
        //                    result = MatchExpression(userContext, accessRule.Code, condition, values);
        //                }

        //                if (!result)
        //                {
        //                    _logger.LogInformation($"user {userContext.UserName} access resource {viewModel.Code} not match the access rule {accessRule.Code} condition {condition}, will be denied!");
        //                    result = false;
        //                    break;
        //                }

        //                _logger.LogDebug($"user {userContext.UserName} access resource {viewModel.Code} in access rule {accessRule.Code} condition {condition} passed!");
        //            }

        //            _logger.LogInformation($"user {userContext.UserName} access resource {viewModel.Code} in access rule {accessRule.Code} condition {accessRule.Condition} all passed!");
        //        }
        //    }
        //}

        //if (result && accessRecord is not null)
        //{
        //    accessRecord.AllowedCount++;
        //    _logger.LogInformation($"user {userAuthorization.UserName} access resource {userAuthorization.Code} all access rules conditions passed!");
        //}

        //if (accessRecord is not null)
        //{
        //    _context.AccessRecords.Update(accessRecord);
        //    await _context.SaveChangesAsync();
        //}

        //return result;

        await Task.CompletedTask;
        return true;
    }
    /// <summary>
    /// 校验 IP 地址
    /// </summary>
    /// <returns></returns>
    private async Task<bool> AuthorizeIpAddress()
    {
        _logger.LogInformation($"{_userAccess}: will check wheather is allow from ip address: {_ipAddress}!");
        if ((_httpContext.User.Identity?.IsAuthenticated ?? false) && _userName == USER_DEVELOPER)
        {
            _logger.LogInformation($"{_userAccess}: is allowed from ip address: {_ipAddress}!");
            return true;
        }

        var ipAddresses = await _cache.GetValueAsync<string[]>(CACHE_APPLICATION_ALLOWEDIPADDRESSES);
        if (ipAddresses is null)
        {
            var parameter = await _context.Parameters.FirstOrDefaultAsync(x => x.Key == PARAMETER_ALLOWEDIPADDRESSES);
            if (parameter is not null && parameter.Value is not null)
            {
                ipAddresses = parameter.Value.Split(CHAR_SEMICOLON, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
                await _cache.SetValueAsync(CACHE_APPLICATION_ALLOWEDIPADDRESSES, ipAddresses, _cacheEntryOptions);
            }
        }

        if (ipAddresses is not null && ipAddresses.Length != 0)
        {
            foreach (var ip in ipAddresses)
            {
                var ipAddressRange = IPAddressRange.Parse(ip);
                var ipAddress = IPAddress.Parse(_ipAddress);
                if (ipAddressRange.Contains(ipAddress))
                {
                    _logger.LogInformation($"{_userAccess}: is allowed from ip address: {_ipAddress}!");
                    return true;
                }
            }
        }

        _logger.LogInformation($"{_userAccess}: is forbidden from ip address: {_ipAddress}!");
        return false;
    }
    /// <summary>
    /// 记录访问次数
    /// </summary>
    /// <param name="userContext">用户上下文</param>
    /// <param name="viewModel">当前资源</param>
    /// <param name="allowedAccess">是否允许访问</param>
    /// <returns></returns>
    private async Task RecordAccessAsync(UserContext userContext, ResourceViewModel viewModel, bool allowedAccess)
    {
        if (userContext.IsAuthenticated && userContext.User.State != DataState.Draft)
        {
            //同一个资源同一个用户非授权类型只会存在唯一一条记录
            var accessRecord = await _context.AccessRecords.FirstOrDefaultAsync(x => x.State == DataState.Available && x.UserId == userContext.UserId && x.ResourceId == viewModel.Id && x.AccessRuleId == null);
            if (accessRecord is null)
            {
                accessRecord = new AccessRecord
                {
                    UserId = userContext.UserId,
                    ResourceId = viewModel.Id
                };

                accessRecord.Create();
                await _context.AccessRecords.AddAsync(accessRecord);
                await _context.SaveChangesAsync();
            }

            accessRecord.AccessCount++;
            if (allowedAccess)
            {
                accessRecord.AllowedCount++;
                _logger.LogInformation($"user {userContext.UserName} access resource {viewModel.Code} all access rules conditions passed!");
            }

            _context.AccessRecords.Update(accessRecord);
            await _context.SaveChangesAsync();
        }
    }
    #endregion

    #region 其他方法
    /// <summary>
    /// @ 开头的参数为调用内部方法进行访问授权, 方法的第一个参数不需要填写, 均为 UserContext
    /// @method params...
    /// </summary>
    /// <param name="userContext">用户上下文</param>
    /// <param name="accessRule">访问规则</param>
    /// <param name="condition">当前条件</param>
    /// <param name="values">条件参数</param>
    /// <returns></returns>
    private bool MatchMethod(UserContext userContext, string accessRule, string condition, params string[] values)
    {
        var method = values[0];
        _logger.LogDebug($"user {userContext.UserName} access rule: {accessRule} condition: {condition} from method: {method}");

        var methodInfo = GetType().GetMethod(method);
        if (methodInfo is not null)
        {
            var parameters = new object[] { userContext };
            if (values.Length > 1)
            {
                parameters.AddRange(values[1..]);
            }

            var returnValue = methodInfo.Invoke(this, parameters);
            if (returnValue != null && returnValue is bool result)
            {
                if (!result)
                {
                    _logger.LogDebug($"user {userContext.UserName} access rule: {accessRule} condition: {condition} from method: {method}, execute result not passed, access denied!");
                }

                return result;
            }
        }

        _logger.LogDebug($"user {userContext.UserName} access rule: {accessRule} condition: {condition} from method: {method}, method not found or params not matched, access denied!");
        return false;
    }
    /// <summary>
    /// 变量构成的表达式
    /// 结构: {field} operator value, 其中 {field} 为路由参数, 用户上下文的参数, 和来自 http 上下文的参数
    /// </summary>
    /// <param name="userContext">用户上下文</param>
    /// <param name="accessRule">访问规则</param>
    /// <param name="condition">当前条件</param>
    /// <param name="values">条件参数</param>
    /// <returns></returns>
    private bool MatchExpression(UserContext userContext, string accessRule, string condition, params string[] values)
    {
        //字段
        var field = values[0];
        //逻辑
        var operatorType = values[1].GetEnumValueByJsonName<OperatorType>();
        //预设值
        var fieldValue = string.Concat(values[2..]);

        _logger.LogDebug($"user {userContext.UserName} access rule: {accessRule} condition: {condition} from variable: {field}, operator: {operatorType}, setting value: {fieldValue}");

        //实际值
        //默认规则形如: field eq value, 根据 field 从请求参数获取参数值, 和 value 进行比较以判断结果
        string? value;

        //{} 参数来自路由或上下文用户信息
        //如: /api/users/getUserRoles/{userLevel} 次数的 userLevel 参数值来自路由, 因此规则可以写为: {userLevel} contains administrator, developer, 表示仅限 administrator 和 developer 用户
        if (_routeValues.ContainsKey(field) && _routeValues.TryGetValue(field, out object? routeValue))
        {
            value = routeValue?.ToString();
            _logger.LogDebug($"user {userContext.UserName} access rule: {accessRule} condition: {condition} from route variable: {field}, operator: {operatorType}, setting value: {fieldValue}, actual value: {value}");
        }
        else
        {
            //1. UserContext value
            value = userContext.GetPropertyValue<UserContext, string>(field);

            //2. HttpContext query option
            value ??= _httpContext.GetFromHttpContext<string>(field);

            //3. HttpRequest value
            value ??= _request.GetPropertyValue<HttpRequest, string>(field);

            //4. HttpContext value
            value ??= _httpContext.GetPropertyValue<HttpContext, string>(field);

            _logger.LogDebug($"user {userContext.UserName} access rule: {accessRule} condition: {condition} from context variable: {field}, operator: {operatorType}, setting value: {fieldValue}, actual value: {value}");
        }

        value ??= string.Empty;

        //比较规则为: 实际值和预设值比较, 如果比较结果为 false, 则表示不满足条件
        var result = operatorType switch
        {
            OperatorType.Equal => value == fieldValue,
            OperatorType.NotEqual => value != fieldValue,
            OperatorType.GreaterThan => Convert.ToDecimal(value) > Convert.ToDecimal(fieldValue),
            OperatorType.GreaterThanAndEqual => Convert.ToDecimal(value) >= Convert.ToDecimal(fieldValue),
            OperatorType.LessThan => Convert.ToDecimal(value) < Convert.ToDecimal(fieldValue),
            OperatorType.LessThanAndEqual => Convert.ToDecimal(value) >= Convert.ToDecimal(fieldValue),
            OperatorType.Contains => value.Contains(fieldValue),
            OperatorType.StartsWith => value.StartsWith(fieldValue),
            OperatorType.EndsWith => value.EndsWith(fieldValue),
            OperatorType.In => fieldValue.Contains(value),
            OperatorType.NotIn => !fieldValue.Contains(value),
            _ => false
        };

        if (!result)
        {
            _logger.LogDebug($"user {userContext.UserName} access rule: {accessRule} condition: {condition} from variable: {field}, operator: {operatorType}, setting value: {fieldValue}, actual value: {value}, check result not passed, access denied!");
        }

        return result;
    }

    /// <summary>
    /// odata 查询表达式不允许出现 select 的 value 值
    /// </summary>
    /// <param name="userContext">用户上下文</param>
    /// <param name="value">select 表达式预设值，为逗号分隔的字符串，表示不可被查询的列名，默认空, 或 *, 表示必须提供 select 选项, 不可以查询全部列</param>
    /// <returns></returns>
    private bool ODataNotAllowSelect(UserContext userContext, string? value = default)
    {
        _logger.LogDebug($"user {userContext.UserName} execute access rule: ODataNotAllowSelect (OData protocol not allow $select expression), parameter: {value}");

        //验证实际值
        var select = _request.GetRequestOption<string>(QUERY_OPTION_SELECT);
        if (string.IsNullOrWhiteSpace(select) || select == CHAR_STAR.ToString())
        {
            return false;
        }

        //验证预设值
        if (string.IsNullOrWhiteSpace(value) || value == CHAR_STAR.ToString())
        {
            //当预设值为全部列，意味实际值只要不为全部列即可
            return (select is not null) && select != CHAR_STAR.ToString();
        }

        //验证实际值其余情况
        return !select.ContainsAny(value, CHAR_COMMA);
    }
    /// <summary>
    /// odata 查询表达式不允许出现 expand 的 value 值
    /// </summary>
    /// <param name="userContext">用户上下文</param>
    /// <param name="value">expand 表达式的预设值, 表示不能出现在 expand 中的选项</param>
    /// <returns></returns>
    private bool ODataNotAllowExpand(UserContext userContext, string? value = default)
    {
        _logger.LogDebug($"user {userContext.UserName} execute access rule: ODataNotAllowExpand (OData protocol not allow $expand expression), parameter: {value}");
        if (string.IsNullOrWhiteSpace(value))
        {
            return true;
        }

        var expand = _request.GetRequestOption<string>(QUERY_OPTION_EXPAND);
        if (!string.IsNullOrWhiteSpace(expand))
        {
            return !expand.ContainsAny(value, CHAR_COMMA);
        }

        return true;
    }
    /// <summary>
    /// 用户是否分配了某角色
    /// </summary>
    /// <param name="userContext">用户上下文</param>
    /// <param name="roles">角色</param>
    /// <returns></returns>
    private bool UserInRoles(UserContext userContext, params string[] roles)
    {
        _logger.LogDebug($"user {userContext.UserName} execute access rule: UserInRoles (whether the user is assigned roles: {roles.ToString(DEFAULT_OUTER_SPLITER)})");
        return userContext.Roles?.Select(x => x.Name?.ToUpperInvariant())?.Intersect(roles.Select(x => x.ToUpperInvariant())).Any() ?? false;
    }
    /// <summary>
    /// 用户是否分配了某群组
    /// </summary>
    /// <param name="userContext">用户上下文</param>
    /// <param name="groups">群组</param>
    /// <returns></returns>
    private bool UserInGroups(UserContext userContext, params string[] groups)
    {
        _logger.LogDebug($"user {userContext.UserName} execute access rule: UserInGroups (whether the user is assigned groups: {groups.ToString(DEFAULT_OUTER_SPLITER)})");
        return userContext.Groups?.Select(x => x.Name?.ToUpperInvariant())?.Intersect(groups.Select(x => x.ToUpperInvariant())).Any() ?? false;
    }
    #endregion
}